package org.cocopapaya.anttsp.domain;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.test.jung.main.Logger;

import edu.uci.ics.jung.graph.Graph;

public class Ant implements QuantitySource {

	private static final double ANT_PHEROMONE = 10.0;

	private static int instances = 0;

	private int id;

	private City currentCity;

	private Collection<City> visitList = new ArrayList<City>();

	private Tour tour = new Tour();;

	public Ant() {
		this.id = instances++;

	}

	public String toString() {
		return this.id + "";
	}

	public void setCurrentCity(City c) {

		this.currentCity = c;
		c.enter(this);

	}

	public boolean hasNext() {
		return !this.visitList.isEmpty();
	}

	public void visitNext() {
		Path exit = findNextExit();

		this.tour.add(exit);

		this.currentCity.leave(this);
		this.visitList.remove(this.currentCity);

		City next = this.currentCity.getOpposite(exit);

		next.enter(this);
		this.currentCity = next;
	}

	public Path findNextExit() {
		//Logger.getInstance().log(this + " finding next exit.");
		Collection<City> choice = new ArrayList<City>();

		Collection<Path> exits = this.currentCity.getExits();

		for (Path ppw : exits) {
			City candidate = this.currentCity.getOpposite(ppw); // this.graph.getOpposite(this.currentCity,
																// ppw);
			if (this.visitList.contains(candidate)) {
				choice.add(candidate);
			}
		}

		//Logger.getInstance().log(this + " have candidates: " + exits);

		ExitChooser chooser;

		if (!choice.isEmpty()) {
			chooser = new PheromoneChooser();

		} else {
			chooser = new VisibilityChooser();
		}

		Path exit = chooser.chooseExit(exits);
		//Logger.getInstance().log(this + " got exit: " + exit);

		return exit;
	}

	abstract class ExitChooser {

		protected static final double BETA = 0;

		public Path chooseExit(Collection<Path> exits) {
			double maxProbability = 0;
			Path chosenExit = null;

			double probalility;

			List<Path> copyExits = new ArrayList<Path>();
			copyExits.addAll(exits);

			Collections.shuffle(copyExits);

			for (Path ppw : copyExits) {
				probalility = calculateProbability(ppw);
				if (probalility > maxProbability) {
					maxProbability = probalility;
					chosenExit = ppw;
				}
			}

			return chosenExit;
		}

		protected abstract double calculateProbability(Path ppw);
	}

	public class PheromoneChooser extends ExitChooser {

		protected static final double ALPHA = 0;

		protected double calculateProbability(Path ppw) {
			return ppw.getPheromone().pow(ALPHA).doubleValue()
					* Math.pow(ppw.getVisibility(), BETA);
		}
	}

	public class VisibilityChooser extends ExitChooser {

		protected double calculateProbability(Path ppw) {

			return Math.pow(ppw.getVisibility(), BETA);
		}
	}

	public Quantity produce() {
		return Quantity.newWithDouble(ANT_PHEROMONE);
	}

	public Tour getTour() {
		return this.tour;
	}

	public void runTour() {
		while (this.hasNext()) {
			this.visitNext();
		}

	}

	public void prepareForTour(City currentCity, Collection<City> cities) {
		this.visitList.addAll(cities);
		this.tour.clear();
		this.setCurrentCity(currentCity);

	}

}
